﻿using System;
using System.ComponentModel;
using System.Diagnostics;
using System.IO;
using System.ServiceModel;
using System.Windows.Forms;
using McTools.Xrm.Connection;
using ICSharpCode.TextEditor;
using JavascriptWebResourcesManager.DelegatesHelpers;
using JavascriptWebResourcesManager.Forms;
using McTools.Xrm.Connection.WinForms;
using Microsoft.Xrm.Sdk;
using Tanguy.WinForm.Utilities.DelegatesHelpers;

namespace JavascriptWebResourcesManager
{
    public partial class MainForm : Form
    {

        #region Variables

        /// <summary>
        /// Connection Manager for Dynamics CRM 2011
        /// </summary>
        ConnectionManager cManager;

        /// <summary>
        /// Connection Statusbar for Dynamics CRM 2011
        /// </summary>
        CrmConnectionStatusBar ccsb;

        private FormHelper _fHelper;

        /// <summary>
        /// Crm organization service
        /// </summary>
        IOrganizationService service;

        /// <summary>
        /// Scripts Manager
        /// </summary>
        ScriptManager sManager;

        /// <summary>
        /// Text editor control
        /// </summary>
        TextEditorControl editorControl;

        #endregion Variables

        #region Constructor

        public MainForm()
        {
            InitializeComponent();
            try
            {
                // Displays version number
                this.Text += " (v" + System.Reflection.Assembly.GetExecutingAssembly().GetName().Version.ToString() + ")";

                // Instanciates the connection manager
                this.cManager = new ConnectionManager();
                this.cManager.ConnectionSucceed += new ConnectionManager.ConnectionSucceedEventHandler(cManager_ConnectionSucceed);
                this.cManager.ConnectionFailed += new ConnectionManager.ConnectionFailedEventHandler(cManager_ConnectionFailed);
                this.cManager.StepChanged += new ConnectionManager.StepChangedEventHandler(cManager_StepChanged);
                this.cManager.RequestPassword += CManagerRequestPassword;

                _fHelper = new FormHelper(this, cManager);

                // Instanciates the connection status bar
                this.ccsb = new CrmConnectionStatusBar(cManager, _fHelper);
                this.Controls.Add(ccsb);

                EditionManager editionManager = new EditionManager();
                editWithVSToolStripMenuItem.Enabled = editionManager.VisualStudio2008Exists;
                editWithVS2010ToolStripMenuItem.Enabled = editionManager.VisualStudio2010Exists;

                editorControl = new TextEditorControl()
                {
                    Width = 537,
                    Height = 369,
                    Left = 6,
                    Top = 45,
                    EnableFolding = true,
                    Anchor = AnchorStyles.Bottom | AnchorStyles.Top | AnchorStyles.Left | AnchorStyles.Right
                };

                this.gbScriptContent.Controls.Add(editorControl);
                editorControl.SetHighlighting("JavaScript");
                editorControl.BringToFront();
                editorControl.MouseWheel += new MouseEventHandler(txtScriptContent_MouseWheel);
                editorControl.TextChanged += new EventHandler(txtScriptContent_TextChanged);

                // Define size
                this.toolStripComboBoxFontSize.SelectedIndex = 2;
            }
            catch (Exception error)
            {
                MessageBox.Show(this, "Error while intializing application: " + error.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }

        bool CManagerRequestPassword(object sender, RequestPasswordEventArgs e)
        {
            return _fHelper.RequestPassword(e.ConnectionDetail);
        }

       #endregion Constructors

        #region Connection Handlers

        void cManager_StepChanged(object sender, StepChangedEventArgs e)
        {
            this.ccsb.SetMessage(e.CurrentStep);
        }

        void cManager_ConnectionFailed(object sender, ConnectionFailedEventArgs e)
        {
            this.ccsb.SetMessage(e.FailureReason);
            MessageBox.Show(e.FailureReason, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
        }

        void cManager_ConnectionSucceed(object sender, ConnectionSucceedEventArgs e)
        {
            this.service = e.OrganizationService;

            this.ccsb.SetConnectionStatus(true, e.ConnectionDetail);
            this.sManager = new ScriptManager(e.OrganizationService);

            if (e.Parameter != null && e.Parameter is String)
            {
                switch (e.Parameter.ToString())
                {
                    case "CreateScript":
                        {
                            this.editorControl.Text = string.Empty;
                            this.tsbNewScript_Click(null, null);
                        }
                        break;
                    case "LoadScripts":
                        {
                            this.editorControl.Text = string.Empty;
                            this.ccsb.SetMessage("Loading scripts...");
                            this.RetrieveScripts();
                        }
                        break;
                    case "Import":
                        {
                            this.Import(false);
                        }
                        break;
                    case "ImportAndPublish":
                        {
                            this.Import(true);
                        }
                        break;
                    case "DoImport":
                        {
                            this.ccsb.SetMessage("Importing script(s)...");
                            CommonDelegates.SetCursor(this, Cursors.WaitCursor);

                            // Disables controls during treatment
                            MethodInvoker mi = delegate
                            {
                                this.tsbLoadScripts.Enabled = false;
                                this.tsbSaveContent.Enabled = false;
                                this.tsddScripts.Enabled = false;
                                this.tsddImportMenu.Enabled = false;
                            };

                            if (this.tsMain.InvokeRequired)
                                this.tsMain.Invoke(mi);
                            else
                                mi();

                            this.DoImport(false);
                        }
                        break;
                    case "DoImportPublish":
                        {
                            this.ccsb.SetMessage("Importing script(s)...");
                            CommonDelegates.SetCursor(this, Cursors.WaitCursor);

                            // Disables controls during treatment
                            MethodInvoker mi = delegate
                            {
                                this.tsbLoadScripts.Enabled = false;
                                this.tsbSaveContent.Enabled = false;
                                this.tsddScripts.Enabled = false;
                                this.tsddImportMenu.Enabled = false;
                            };

                            if (this.tsMain.InvokeRequired)
                                this.tsMain.Invoke(mi);
                            else
                                mi();

                            this.DoImport(true);
                        }
                        break;
                }
            }
            else if (e.Parameter != null && e.Parameter is Entity)
            {
                this.sManager.CreateScript((Entity)e.Parameter);
                this.tsbLoadScripts_Click(null, null);
            }
        }

        #endregion

        #region Methods

        #region Scripts Loading

        private void tsbNewScript_Click(object sender, EventArgs e)
        {
            if (this.service == null)
            {
                this._fHelper.AskForConnection("CreateScript");
            }
            else
            {
                CreateScriptForm csForm = new CreateScriptForm(this.service);
                csForm.StartPosition = FormStartPosition.CenterParent;

                if (csForm.ShowDialog() == DialogResult.OK)
                {
                    tsbLoadScripts_Click(null, null);
                }
            }
        }

        private void tsbLoadScripts_Click(object sender, EventArgs e)
        {
            if (this.service == null)
            {
                this.tvScripts.Nodes.Clear();

                this._fHelper.AskForConnection("LoadScripts");
            }
            else
            {
                this.editorControl.Text = string.Empty;
                this.tvScripts.Nodes.Clear();
                this.tvScripts.Enabled = false;
                GroupBoxDelegates.SetEnableState(this.gbScriptContent, false);

                BackgroundWorker bwFillScripts = new BackgroundWorker();
                bwFillScripts.DoWork += new DoWorkEventHandler(bwFillScripts_DoWork);
                bwFillScripts.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bwFillScripts_RunWorkerCompleted);
                bwFillScripts.RunWorkerAsync();
            }
        }

        void bwFillScripts_DoWork(object sender, DoWorkEventArgs e)
        {
            this.RetrieveScripts();
        }

        void bwFillScripts_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            if (e.Error != null)
            {
                string errorMessage = CrmExceptionHelper.GetErrorMessage(e.Error, true);
                CommonDelegates.DisplayMessageBox(this, errorMessage, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
            else
            {
                CommonDelegates.SetEnableState(this.tvScripts, true);
            }

        }

        private void RetrieveScripts()
        {
            EntityCollection scripts = this.sManager.RetrieveScripts();

            GroupBoxDelegates.SetEnableState(this.gbScripts, true);
            this.ccsb.SetMessage(string.Empty);

            TreeViewDelegates.ClearNodes(this.tvScripts);

            foreach (Entity script in scripts.Entities)
            {
                Script scriptObject = new Script(script);

                string[] nameParts = script["name"].ToString().Split('/');

                this.AddNode(nameParts, 0, this.tvScripts, scriptObject);
            }

            TreeViewDelegates.ExpandAll(this.tvScripts);

            if (this.panelWarning.Visible)
            {
                this.panelWarning.Visible = false;
                this.gbScriptContent.Height += 32;
            }
        }

        private void AddNode(string[] nameParts, int index, object parent, Script scriptObject)
        {
            if (index == 0)
            {
                if (((TreeView)parent).Nodes.Find(nameParts[index], false).Length == 0)
                {
                    TreeNode node = new TreeNode(nameParts[index]);
                    node.Name = node.Text;

                    if (index == nameParts.Length - 1)
                    {
                        node.Tag = scriptObject;
                        node.ImageIndex = 2;
                        node.SelectedImageIndex = 2;
                    }

                    TreeViewDelegates.AddNode((TreeView)parent, node);

                    this.AddNode(nameParts, index + 1, node, scriptObject);
                }
                else
                {
                    this.AddNode(nameParts, index + 1, ((TreeView)parent).Nodes.Find(nameParts[index], false)[0], scriptObject);
                }
            }
            else if (index < nameParts.Length)
            {
                if (((TreeNode)parent).Nodes.Find(nameParts[index], false).Length == 0)
                {
                    TreeNode node = new TreeNode(nameParts[index]);
                    node.Name = node.Text;

                    if (index == nameParts.Length - 1)
                    {
                        node.Tag = scriptObject;
                        node.ImageIndex = 2;
                        node.SelectedImageIndex = 2;
                    }
                    else
                    {
                        if (index == 0)
                        {
                            node.ImageIndex = 0;
                            node.SelectedImageIndex = 0;
                        }
                        else
                        {
                            node.ImageIndex = 1;
                            node.SelectedImageIndex = 1;
                        }
                    }

                    TreeViewDelegates.AddNode((TreeNode)parent, node);
                    this.AddNode(nameParts, index + 1, node, scriptObject);
                }
                else
                {
                    this.AddNode(nameParts, index + 1, ((TreeNode)parent).Nodes.Find(nameParts[index], false)[0], scriptObject);
                }
            }
        }

        #endregion Scripts Loading

        #region Scripts Events

        private void txtScriptContent_TextChanged(object sender, EventArgs e)
        {
            TreeNode selectedNode = TreeViewDelegates.GetSelectedNode(this.tvScripts);

            if (selectedNode != null && selectedNode.Tag != null)
            {
                Entity script = ((Script)selectedNode.Tag).ScriptEntity;

                // Get original and current script content
                string originalContent = script.Contains("content") ? ScriptManager.GetContentFromBase64String(script["content"].ToString()) : string.Empty;
                //string currentContent = this.txtScriptContent.Text;
                string currentContent = this.editorControl.Text;

                // If contents different, allow to save
                if (originalContent != currentContent)
                {
                    this.tsbSaveContent.Enabled = true;
                }
                else
                {
                    this.tsbSaveContent.Enabled = false;
                }
            }
            else
            {
                this.tsbSaveContent.Enabled = false;
            }
        }

        private void txtScriptContent_MouseWheel(object sender, MouseEventArgs e)
        {
            // Check if CTRL key is pressed
            if (!System.Windows.Forms.Control.ModifierKeys.HasFlag(Keys.Control))
                return;

            try
            {
                this.toolStripComboBoxFontSize.SelectedIndex += (e.Delta / 20);
            }
            catch
            {
                if (this.toolStripComboBoxFontSize.SelectedIndex == -1)
                    this.toolStripComboBoxFontSize.SelectedIndex = 0;
                else this.toolStripComboBoxFontSize.SelectedIndex = this.toolStripComboBoxFontSize.Items.Count - 1;
            }
        }

        private void tsbSaveContent_Click(object sender, EventArgs e)
        {
            if (this.tvScripts.SelectedNode != null)
            {
                Script item = (Script)this.tvScripts.SelectedNode.Tag;

                // Save script content to web resource entity
                //item.ScriptEntity["content"] = ScriptManager.GetBase64StringFromString(this.txtScriptContent.Text);
                item.ScriptEntity["content"] = ScriptManager.GetBase64StringFromString(this.editorControl.Text);

                // If filepath is defined, save the content to the file
                if (!string.IsNullOrEmpty(item.FilePath))
                {
                    using (StreamWriter writer = new StreamWriter(item.FilePath, false))
                    {
                        //writer.WriteLine(this.txtScriptContent.Text);
                        writer.WriteLine(this.editorControl.Text);
                    }
                }

                // Disbale saving action
                this.tsbSaveContent.Enabled = false;
            }
        }

        private void toolStripComboBoxFontSize_SelectedIndexChanged(object sender, EventArgs e)
        {
            System.Drawing.Font newFont = new System.Drawing.Font(this.editorControl.Font.Name, float.Parse(this.toolStripComboBoxFontSize.SelectedItem.ToString().Split(' ')[0]));

            //this.txtScriptContent.Font = newFont;
            this.editorControl.Font = newFont;
        }

        #endregion Scripts Events

        #region Event handlers

        private void chkSelectAll_CheckedChanged(object sender, EventArgs e)
        {
            tvScripts.AfterCheck -= new TreeViewEventHandler(treeView1_AfterCheck);

            foreach (TreeNode node in this.tvScripts.Nodes)
                node.Checked = this.chkSelectAll.Checked;

            tvScripts.AfterCheck += new TreeViewEventHandler(treeView1_AfterCheck);
        }

        private void treeView1_AfterCheck(object sender, TreeViewEventArgs e)
        {
            tvScripts.AfterCheck -= new TreeViewEventHandler(treeView1_AfterCheck);

            TreeNode currentNode = e.Node;

            // Manage parent and childs TreeNode checked state
            this.ChangeChildNodeCheckStatus(currentNode, currentNode.Checked);
            this.ChangeParentNodeCheckStatus(currentNode, currentNode.Checked);

            // Define Import Menu enabled state regarding number of checked elements
            int checkedNodeCount = currentNode.Checked ? 1 : 0;

            foreach (TreeNode node in currentNode.Nodes)
                checkedNodeCount = this.CountCheckNodes(node, checkedNodeCount);

            this.tsddImportMenu.Enabled = checkedNodeCount > 0;

            tvScripts.AfterCheck += new TreeViewEventHandler(treeView1_AfterCheck);
        }

        private void treeView1_AfterSelect(object sender, TreeViewEventArgs e)
        {
            this.editorControl.ResetText();
            
            if (this.tvScripts.SelectedNode != null && this.tvScripts.SelectedNode.Tag != null)
            {
                // Displays script content
                Entity script = ((Script)this.tvScripts.SelectedNode.Tag).ScriptEntity;

                if (script.Contains("content"))
                {
                    this.editorControl.Text = ScriptManager.GetContentFromBase64String(script["content"].ToString());
                    this.tvScripts.SelectedNode.ContextMenuStrip = this.scriptContextMenuStrip;
                }

                this.gbScriptContent.Enabled = true;
            }
            else
            {
                // Clear script content
                this.editorControl.Text = string.Empty;
                this.gbScriptContent.Enabled = false;
                this.tvScripts.SelectedNode.ContextMenuStrip = null;
            }

            this.editorControl.Refresh();
        }

        private void treeView1_MouseDown(object sender, MouseEventArgs e)
        {
            if (e.Button == MouseButtons.Right)
            {
                // Select the clicked node
                tvScripts.SelectedNode = tvScripts.GetNodeAt(e.X, e.Y);

                if (tvScripts.SelectedNode != null && tvScripts.SelectedNode.Nodes.Count == 0)
                {
                    this.scriptContextMenuStrip.Show(tvScripts, e.Location);
                }
            }
        }

        #endregion Event handlers

        #region Menu handlers

        private void tsbUpdateScriptMetadata_Click(object sender, EventArgs e)
        {
            if (this.tvScripts.SelectedNode != null)
            {
                // Get script object
                Script script = (Script)this.tvScripts.SelectedNode.Tag;

                // Launches metadata form
                UpdateForm form = new UpdateForm(script, this.service);
                form.StartPosition = FormStartPosition.CenterParent;

                if (form.ShowDialog(this) == DialogResult.OK)
                {
                    // Updates script
                    this.tvScripts.SelectedNode.Tag = form.Script;
                }
            }
        }

        private void saveCheckedScriptsToFilesToolStripMenuItem_Click(object sender, EventArgs e)
        {
            int checkedNodeCount = 0;

            foreach (TreeNode node in this.tvScripts.Nodes)
                checkedNodeCount = this.CountCheckNodes(node, checkedNodeCount);

            if (checkedNodeCount == 0)
            {
                MessageBox.Show(this, "No script to save!", "Warning", MessageBoxButtons.OK, MessageBoxIcon.Warning);
                return;
            }

            ToolSettings ts = new ToolSettings();

            // Let user decide where to save files
            FolderBrowserDialog fbd = new FolderBrowserDialog()
            {
                Description = "Select a folder where to save scripts files",
                ShowNewFolderButton = true,
                //RootFolder = Environment.SpecialFolder.Desktop,
                SelectedPath = ts.FolderPath
            };

            if (fbd.ShowDialog() == DialogResult.OK)
            {
                ts.FolderPath = fbd.SelectedPath;
                string folderPath = fbd.SelectedPath;

                foreach (TreeNode node in this.tvScripts.Nodes)
                {
                    if (node.Checked)
                    {
                        this.CreateItem(node, folderPath);
                    }
                }

                MessageBox.Show(this, "Scripts saved to " + folderPath, "Information", MessageBoxButtons.OK, MessageBoxIcon.Information);
            }
        }

        private void loadScriptsFromDiskToolStripMenuItem_Click(object sender, EventArgs e)
        {
            ToolSettings ts = new ToolSettings();

            // Let the user decides where to find files
            FolderBrowserDialog fbd = new FolderBrowserDialog()
            {
                Description = "Select the folder where the scripts files are located",
                ShowNewFolderButton = true,
                SelectedPath = ts.FolderPath
            };

            if (fbd.ShowDialog() == DialogResult.OK)
            {
                ts.FolderPath = fbd.SelectedPath;
                this.tvScripts.Nodes.Clear();

                DirectoryInfo di = new DirectoryInfo(fbd.SelectedPath);

                foreach (DirectoryInfo diChild in di.GetDirectories("*_", SearchOption.TopDirectoryOnly))
                {
                    TreeNode rootFolderNode = new TreeNode(diChild.Name);
                    rootFolderNode.ImageIndex = 0;

                    this.tvScripts.Nodes.Add(rootFolderNode);

                    this.CreateFolderStructure(rootFolderNode, diChild);
                }

                foreach (FileInfo fiChild in di.GetFiles("*.js", SearchOption.TopDirectoryOnly))
                {
                    this.CreateScriptNode(fiChild, this.tvScripts);
                }

                this.tvScripts.ExpandAll();

                this.gbScripts.Enabled = true;

                if (!this.panelWarning.Visible)
                {
                    this.panelWarning.Visible = true;
                    this.gbScriptContent.Height -= 32;
                }
            }
        }

        private void importCheckedScriptsToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (this.service == null)
            {
                this._fHelper.AskForConnection("Import");
            }
            else
            {
                this.Import(false);
            }
        }

        private void importAndPublishCheckedScriptsToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (this.service == null)
            {
                this._fHelper.AskForConnection("ImportAndPublish");
            }
            else
            {
                this.Import(true);
            }
        }

        private void tsbAbout_Click(object sender, EventArgs e)
        {
            AboutForm aForm = new AboutForm();
            aForm.StartPosition = FormStartPosition.CenterParent;
            aForm.ShowDialog();
        }

        #endregion Menu handlers

        #region Import and Publish

        private void Import(bool publish)
        {
            if (this.service == null)
            {
                if (publish)
                    this._fHelper.AskForConnection("DoImportPublish");
                else
                    this._fHelper.AskForConnection("DoImport");
            }
            else
            {
                this.ccsb.SetMessage("Importing script(s)...");
                CommonDelegates.SetCursor(this, Cursors.WaitCursor);

                // Disables controls during treatment
                MethodInvoker mi = delegate
                {
                    this.tsbNewScript.Enabled = false;
                    this.tsbLoadScripts.Enabled = false;
                    this.tsbSaveContent.Enabled = false;
                    this.tsddScripts.Enabled = false;
                    this.tsddImportMenu.Enabled = false;
                };

                if (this.tsMain.InvokeRequired)
                    this.tsMain.Invoke(mi);
                else
                    mi();

                // Launches treatment
                BackgroundWorker importWorker = new BackgroundWorker();
                importWorker.DoWork += new DoWorkEventHandler(importWorker_DoWork);
                importWorker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(importWorker_RunWorkerCompleted);
                importWorker.RunWorkerAsync(publish);
            }
        }

        private void importWorker_DoWork(object sender, DoWorkEventArgs e)
        {
            this.DoImport((bool)e.Argument);
        }

        private void importWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            this.ccsb.SetMessage("Done!");

            MethodInvoker mi1 = delegate
            {
                this.Cursor = Cursors.Default;
            };

            if (this.InvokeRequired)
                this.Invoke(mi1);
            else
                mi1();

            // Reactivate controls
            MethodInvoker mi2 = delegate
            {
                this.tsbNewScript.Enabled = true;
                this.tsbLoadScripts.Enabled = true;
                this.tsbSaveContent.Enabled = true;
                this.tsddScripts.Enabled = true;
                this.tsddImportMenu.Enabled = true;
            };

            if (this.tsMain.InvokeRequired)
                this.tsMain.Invoke(mi2);
            else
                mi2();
        }

        private void DoImport(bool publish)
        {
            foreach (TreeNode node in TreeViewDelegates.GetNodes(this.tvScripts))
            {
                this.ManageNode(node, publish);
            }

            this.ccsb.SetMessage(string.Empty);
        }

        private void ManageNode(TreeNode node, bool publish)
        {
            if (node.Checked && node.Nodes.Count == 0)
            {
                // Retrieve web resources
                Script scriptObject = (Script)node.Tag;
                Entity script = scriptObject.ScriptEntity;

                // If a filepath is defined, the script content is reloaded
                if (!string.IsNullOrEmpty(scriptObject.FilePath))
                {
                    using (StreamReader reader = new StreamReader(scriptObject.FilePath))
                    {
                        string content = reader.ReadToEnd();

                        script["content"] = ScriptManager.GetBase64StringFromString(content);

                        // If the current item is selected, the content displayed is refreshed
                        if (TreeViewDelegates.IsSelected(node))
                        {
                            TextBoxDelegates.SetText(this.editorControl, content);
                            //CommonDelegates.SetEnableState(this.tsbSaveContent.Owner, false);
                        }
                    }
                }

                try
                {
                    // If the virtual web resource contains the id, the web resource can be
                    // updated. If not, we need to retrieve the web resource first
                    if (script.Contains("webresourceid"))
                    {
                        this.ccsb.SetMessage("Updating script " + script["name"].ToString());
                        this.sManager.UpdateScript(script);
                    }
                    else
                    {
                        // Retrieve the web resource by its name
                        Entity currentScript = this.sManager.RetrieveScript(script["name"].ToString());

                        // If the script was not retrieved, it is not existing on 
                        // the server. It must be created.
                        if (currentScript == null)
                        {
                            this.ccsb.SetMessage("Creating script " + script["name"].ToString());

                            Guid id = this.sManager.CreateScript(script);
                            script["webresourceid"] = id;
                        }
                        else
                        {
                            if (!currentScript.Contains("displayname") || currentScript["displayname"].ToString().Length == 0)
                            {
                                currentScript["displayname"] = script.Contains("displayname") ? script["displayname"] : "";
                            }

                            currentScript["content"] = script["content"];

                            this.sManager.UpdateScript(currentScript);

                            script = currentScript;
                        }
                    }

                    this.ccsb.SetMessage("Saved!");

                    // If publish is required
                    if (publish)
                    {
                        this.ccsb.SetMessage("Publishing script " + script["name"].ToString() + "...");
                        this.sManager.PublishScript(script);
                        this.ccsb.SetMessage("Done!");
                    }
                }
                catch (FaultException<IOrganizationService> error)
                {
                    string message = string.Format("Error while importing script '{0}': {1}", script["name"].ToString(), error.Message);
                    CommonDelegates.DisplayMessageBox(this, message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
                }
            }
            else
            {
                foreach (TreeNode childNode in node.Nodes)
                {
                    this.ManageNode(childNode, publish);
                }
            }
        }

        #endregion Import and Publish

        #region Context Menu handlers

        private void saveFileToDiskToolStripMenuItem_Click(object sender, EventArgs e)
        {
            // If no item is selected, return
            if (this.tvScripts.SelectedNode == null)
                return;

            // Uncheck all items
            foreach (TreeNode node in this.tvScripts.Nodes)
            {
                node.Checked = false;
            }

            // Check the selected item
            this.tvScripts.SelectedNode.Checked = true;

            // As for script save
            this.saveCheckedScriptsToFilesToolStripMenuItem_Click(this, null);
        }

        private void importAndPublishScriptToolStripMenuItem_Click(object sender, EventArgs e)
        {
            // If no item is selected, return
            if (this.tvScripts.SelectedNode == null)
                return;

            // Uncheck all items
            foreach (TreeNode node in this.tvScripts.Nodes)
            {
                node.Checked = false;
            }

            // Check the selected item
            this.tvScripts.SelectedNode.Checked = true;

            // Launch import and publish
            this.Import(true);
        }

        private void importScriptToolStripMenuItem_Click(object sender, EventArgs e)
        {
            // If no item is selected, return
            if (this.tvScripts.SelectedNode == null)
                return;

            // Uncheck all items
            foreach (TreeNode node in this.tvScripts.Nodes)
            {
                node.Checked = false;
            }

            // Check the selected item
            this.tvScripts.SelectedNode.Checked = true;

            // Launche import
            this.Import(false);
        }

        private void EditNotepadToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (this.tvScripts.SelectedNode == null)
                return;

            Script currentScript = (Script)this.tvScripts.SelectedNode.Tag;

            // Check if current selected script has link to a file
            if (string.IsNullOrEmpty(currentScript.FilePath))
            {
                MessageBox.Show(this, "This script doesn't have any copy on disk!\r\n\r\nSave this script to disk before trying to edit it.", "", MessageBoxButtons.OK, MessageBoxIcon.Warning);
            }
            else
            {
                // Launch edit with default text editor
                EditionManager eManager = new EditionManager();
                eManager.LaunchDefaultTextEditor(currentScript.FilePath);
            }
        }

        private void editWithVSToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (this.tvScripts.SelectedNode == null)
                return;

            Script currentScript = (Script)this.tvScripts.SelectedNode.Tag;

            // Check if current selected script has link to a file
            if (string.IsNullOrEmpty(currentScript.FilePath))
            {
                MessageBox.Show(this, "This script doesn't have any copy on disk!\r\n\r\nSave this script to disk before trying to edit it.", "", MessageBoxButtons.OK, MessageBoxIcon.Warning);
            }
            else
            {
                // Launch edit with Visual Studio
                EditionManager eManager = new EditionManager();
                eManager.LaunchVisualStudio2008(currentScript.FilePath);
            }
        }

        private void editWithVS2010ToolStripMenuItem_Click(object sender, EventArgs e)
        {
            if (this.tvScripts.SelectedNode == null)
                return;

            Script currentScript = (Script)this.tvScripts.SelectedNode.Tag;

            // Check if current selected script has link to a file
            if (string.IsNullOrEmpty(currentScript.FilePath))
            {
                MessageBox.Show(this, "This script doesn't have any copy on disk!\r\n\r\nSave this script to disk before trying to edit it.", "", MessageBoxButtons.OK, MessageBoxIcon.Warning);
            }
            else
            {
                // Launch edit with Visual Studio
                EditionManager eManager = new EditionManager();
                eManager.LaunchVisualStudio2010(currentScript.FilePath);
            }
        }

        #endregion Context menu handlers

        #region Folders and TreeNodes methods

        /// <summary>
        /// Create an item (script file or folder) on disk depending on specifed
        /// TreeNode
        /// </summary>
        /// <param name="node">TreeNode element</param>
        /// <param name="folderPath">Current physical path</param>
        private void CreateItem(TreeNode node, string folderPath)
        {
            // If current node has child, it is a folder, not a script
            if (node.Nodes.Count > 0)
            {
                bool doContinue = false;

                // Check if current node has child nodes checked. If not, no need
                // to create a folder on disk
                foreach (TreeNode childNode in node.Nodes)
                {
                    if (childNode.Checked)
                    {
                        doContinue = true;
                        break;
                    }
                }

                if (doContinue)
                {
                    Directory.CreateDirectory(folderPath + "\\" + node.Text);

                    foreach (TreeNode childNode in node.Nodes)
                    {
                        if (node.Checked)
                        {
                            this.CreateItem(childNode, folderPath + "\\" + node.Text);
                        }
                    }
                }
            }
            else
            {
                // If checked, create a script file on disk
                if (node.Checked)
                {
                    Entity script = ((Script)node.Tag).ScriptEntity;

                    using (StreamWriter writer = new StreamWriter(folderPath + "\\" + node.Text, false))
                    {
                        if (script.Contains("content"))
                            writer.Write(ScriptManager.GetContentFromBase64String(script["content"].ToString()));
                        else
                            writer.WriteLine(string.Empty);
                    }

                    ((Script)node.Tag).FilePath = folderPath + "\\" + node.Text;
                }
            }
        }

        /// <summary>
        /// Create a TreeView folder structure depending on the DirectoryInfo
        /// element in parameters
        /// </summary>
        /// <param name="parentFolderNode">Current TreeNode</param>
        /// <param name="di">Current physical directory info</param>
        private void CreateFolderStructure(TreeNode parentFolderNode, DirectoryInfo di)
        {
            foreach (DirectoryInfo diChild in di.GetDirectories())
            {
                // If the current physical directory has sub directories or 
                // javascript file, a new TreeNode has to be created
                if (diChild.GetDirectories().Length > 0 || diChild.GetFiles("*.js").Length > 0)
                {
                    TreeNode folderNode = new TreeNode(diChild.Name);
                    folderNode.ImageIndex = 1;
                    folderNode.SelectedImageIndex = 1;

                    parentFolderNode.Nodes.Add(folderNode);

                    this.CreateFolderStructure(folderNode, diChild);
                }
            }

            foreach (FileInfo fiChild in di.GetFiles("*.js", SearchOption.TopDirectoryOnly))
            {
                // Create a TreeNode for each javascript file
                this.CreateScriptNode(fiChild, parentFolderNode);
            }
        }

        /// <summary>
        /// Create a TreeNode for the javascript file passed in parameter
        /// </summary>
        /// <param name="fiChild">Javascript FileInfo</param>
        /// <param name="parent">Parent element (TreeView or TreeNode)</param>
        private void CreateScriptNode(FileInfo fiChild, object parent)
        {
            string scriptId = string.Empty;
            string scriptName = fiChild.Name;
            string fileName = scriptName;

            // Creating the script "name" attribute which depends on the current
            // TreeView structure
            if (parent is TreeNode)
            {
                TreeNode parentNode = (TreeNode)parent;

                while (parentNode != null && !parentNode.Text.EndsWith("_"))
                {
                    fileName = parentNode.Text + "/" + fileName;
                    parentNode = parentNode.Parent;
                }

                fileName = parentNode.Text + "/" + fileName;
            }

            // Create new virtual web resource
            Entity script = new Entity("webresource");
            script["name"] = fileName;

            // Add content
            using (StreamReader reader = new StreamReader(fiChild.FullName))
            {
                script["content"] = ScriptManager.GetBase64StringFromString(reader.ReadToEnd());
            }

            Script scriptObject = new Script(script, fiChild.FullName);

            TreeNode node = new TreeNode()
            {
                Text = scriptName,
                Tag = scriptObject,
                ImageIndex = 2,
                SelectedImageIndex = 2
            };

            if (parent is TreeNode)
            {
                ((TreeNode)parent).Nodes.Add(node);
            }
            else
            {
                ((TreeView)parent).Nodes.Add(node);
            }
        }

        #endregion Folders and TreeNodes methods

        /// <summary>
        /// Propagate the current node checked state to the child nodes
        /// </summary>
        /// <param name="node">Current TreeNode</param>
        /// <param name="status">Checked state</param>
        private void ChangeChildNodeCheckStatus(TreeNode node, bool status)
        {
            node.Checked = status;

            foreach (TreeNode childNode in node.Nodes)
            {
                this.ChangeChildNodeCheckStatus(childNode, status);
            }
        }

        /// <summary>
        /// Propagate current node checked state to its parent node
        /// </summary>
        /// <param name="node">Current TreeNode</param>
        /// <param name="status">Checked state</param>
        private void ChangeParentNodeCheckStatus(TreeNode node, bool status)
        {
            node.Checked = status;

            if (node.Parent != null)
            {
                int childNodesCheckedCount = 0;
                childNodesCheckedCount = this.CountCheckNodes(node.Parent, childNodesCheckedCount);

                if (status == false)
                {
                    if (childNodesCheckedCount == 1)
                    {
                        node.Parent.Checked = false;
                        this.ChangeParentNodeCheckStatus(node.Parent, status);
                    }
                }
                else
                {
                    node.Parent.Checked = true;
                    this.ChangeParentNodeCheckStatus(node.Parent, status);
                }
            }
        }

        /// <summary>
        /// Counts the number of checked TreeNodes
        /// </summary>
        /// <param name="node">Current TreeNode</param>
        /// <param name="count">Number of checked TreeNodes</param>
        /// <returns>Number of checked TreeNodes updated</returns>
        private int CountCheckNodes(TreeNode node, int count)
        {
            if (node.Checked)
                count++;

            foreach (TreeNode childNode in node.Nodes)
                count = this.CountCheckNodes(childNode, count);

            return count;
        }

        #endregion Methods

        private void tsbRate_Click(object sender, EventArgs e)
        {
            Process.Start("http://jswebresourcemanager.codeplex.com/releases");
        }

        private void tsbDiscuss_Click(object sender, EventArgs e)
        {
            Process.Start("http://jswebresourcemanager.codeplex.com/discussions");
        }

        private void tsbReportBug_Click(object sender, EventArgs e)
        {
            Process.Start("http://jswebresourcemanager.codeplex.com/WorkItem/Create");
        }

        private void tsbDonate_Click(object sender, EventArgs e)
        {
            string url = "";

            string business = "tanguy92@hotmail.com";  // your paypal email
            string description = "Donation%20for%20MSCRM%20Tools";            // '%20' represents a space. remember HTML!
            string country = "FR";                  // AU, US, etc.
            string currency = "EUR";                 // AUD, USD, etc.

            url += "https://www.paypal.com/cgi-bin/webscr" +
                "?cmd=" + "_donations" +
                "&business=" + business +
                "&lc=" + country +
                "&item_name=" + description +
                "&currency_code=" + currency +
                "&bn=" + "PP%2dDonationsBF";

            System.Diagnostics.Process.Start(url);

        }

        private void tsbDefineProxySettings_Click(object sender, EventArgs e)
        {
            this._fHelper.EditProxySettings(this, cManager.ConnectionsList);
        }

        private void MainForm_Resize(object sender, EventArgs e)
        {
            this.splitContainer1.FixedPanel = FixedPanel.Panel1;
        }

      
    }
}
